﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using log4net;
using PortableVersion.Core.General;

namespace PortableVersion.Core.FileSystem.Pvml
{
    public class PvmlMetadataService : IMetadataService<FileItem> 
    {
        private readonly ILog _log;
        private readonly List<FileItem> _items = new List<FileItem>();
        private const string DefaultNs = "";

        public void AddItem(FileItem item)
        {
            _items.Add(item);
        }

        public void AddItemRange(IEnumerable<FileItem> itemRange)
        {
            _items.AddRange(itemRange);
        }

        public void Clear()
        {
            _items.Clear();
        }

        public void Save(String path)
        {
            path.AssertNotNull("path");
            _log.InfoFormat("Saving Pvml to {0}", path);
            var doc = new XDocument();
            var root = NewElement("FolderSource");
            doc.Add(root);
            var files = NewElement("Files");
            var folders = NewElement("Folders");
            //Master path can be used to find the master directory
            var masterPath = NewElement("Master");
            masterPath.Add(new XAttribute("Path", MasterPath));
            _items.Where(i => i.IsFolder).Select(NewVersioningItem).ToList()
                .ForEach(folders.Add);
            _items.Where(i => !i.IsFolder).Select(NewVersioningItem).ToList()
                .ForEach(files.Add);
            root.Add(masterPath);
            root.Add(folders);
            root.Add(files);
            doc.Save(path);
            _log.Info("Pvml save completed.");
        }

        private static XElement NewVersioningItem(IVersioningItem versioningItem)
        {
            var rv = NewElement("Item");
            rv.Add(new XAttribute("Name", versioningItem.Name));
            rv.Add(new XAttribute("Key", versioningItem.Key));
            rv.Add(new XAttribute("Path", versioningItem.Path));
            rv.Add(new XAttribute("ModifiedDate", versioningItem.ModifiedDate));
            return rv;
        }

        private static XElement NewElement(string name)
        {
            return new XElement(GetName(name));
        }

        private static XName GetName(string name)
        {
            return XName.Get(name, DefaultNs);
        }

        private static FileItem GetFileItem(XElement elem, bool isDir)
        {
            //Pvml metadata is only for the slave
            return FileItem.CreateFrom(
                (string) elem.Attribute(GetName("Key")),
                (string) elem.Attribute(GetName("Path")),
                (DateTime) elem.Attribute(GetName("ModifiedDate")),
                isDir,
                FilePosition.Slave
                );
        }

        public void Load(string path)
        {
            _log.InfoFormat("Loading pvml from {0}", path);
            var doc = XDocument.Load(path);
            MasterPath = (string)doc.Descendants(GetName("Master")).First().Attribute(GetName("Path"));
            var files = doc.Descendants(GetName("Files")).First().Descendants(GetName("Item"));
            var folders = doc.Descendants(GetName("Folders")).First().Descendants(GetName("Item"));
            var items = new List<FileItem>();
            items.AddRange(files.Select(f => GetFileItem(f, false)));
            items.AddRange(folders.Select(f => GetFileItem(f, true)));
            Clear();
            AddItemRange(items.OrderBy(i => i.Path));
            _log.InfoFormat("Pvml load completed");
        }

        public string MasterPath { get; private set; }

        public PvmlMetadataService(string masterPath, ILog log)
        {
            MasterPath = masterPath;
            _log = log;
        }

        public IEnumerable<FileItem> Items
        {
            get { return _items; }
        }
    }
}
